package org.knot.autojmx.api;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.knot.autojmx.AnnotationAutoJMXHandler;
import org.knot.autojmx.AutoJMXHandler;
import org.knot.autojmx.annotations.AutoJMX;
import org.knot.autojmx.exception.AutoJMXException;

/**
 * 通过API方式发布MBean
 * 
 * @author <a href="mailto:yuxiaoweimail@gmail.com">yuxiaowei</a>
 * 
 */
public class AutoJMXAPI {

	private final static AutoJMXAPI _autojmxapi = new AutoJMXAPI();

	private AutoJMXAPI() {
	}

	public static AutoJMXAPI Instance() {
		return _autojmxapi;
	}

	// Micro container context of bean
	private static ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<String, Object>();

	// Micro container context of MBean
	private static ConcurrentHashMap<String, AutoJMXHandler> handlers = new ConcurrentHashMap<String, AutoJMXHandler>();

	public final static ConcurrentHashMap<String, AutoJMXHandler> getHandlers() {
		return handlers;
	}

	/**
	 * get all Mbean
	 * 
	 * @return
	 */
	public final static ConcurrentHashMap<String, Object> getBeans() {
		return beans;
	}

	public <T> T getBean(Class<T> clazz) {
		if(!beans.containsKey(clazz.getName())){
			for (Map.Entry<String, Object> m : beans.entrySet()) {
				if (m.getValue().getClass().getName().equals(clazz.getName())) {
					return (T) m.getValue();
				}
			}
		}
		return (T) beans.get(clazz.getName());
	}

	public <T> T getBean(String name) {
		return (T) beans.get(name);
	}

	/**
	 * create Mbean
	 * 
	 * @param <T>
	 * @param clazz
	 *            pojo bean class
	 * @param name
	 *            pojo bean name
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public <T> T createJMXBean(Class<T> clazz, String name)
			throws InstantiationException, IllegalAccessException,
			SecurityException, IllegalArgumentException, AutoJMXException,
			NoSuchMethodException, InvocationTargetException {
		return createJMXBean(clazz, null, null, name, null);
	}

	/**
	 * 创建Mbean
	 * 
	 * @param <T>
	 * @param clazz
	 *            pojo bean'class
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> T createJMXBean(Class<T> clazz) throws InstantiationException,
			IllegalAccessException, SecurityException,
			IllegalArgumentException, AutoJMXException, NoSuchMethodException,
			InvocationTargetException {
		return createJMXBean(clazz, null, null, clazz.getName(), null);
	}

	/**
	 * 创建Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> T createJMXBean(T bean) throws AutoJMXException {
		return createJMXBean(bean, null, null);
	}

	/**
	 * 创建Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> T createJMXBean(T bean, String beanName) throws AutoJMXException {
		return createJMXBean(bean, beanName, null);
	}

	/**
	 * create Mbean
	 * 
	 * @param <T>
	 * @param clazz
	 *            pojo bean class
	 * @param parameterTypes
	 *            pojo bean Constructor's parameterTypes
	 * @param initargs
	 *            pojo bean Constructor's initargs
	 * @param name
	 *            pojo bean name
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public <T> T createJMXBean(Class<T> clazz, Class[] parameterTypes,
			Object[] initargs, String name, AutoJMX autoJMX)
			throws InstantiationException, IllegalAccessException,
			AutoJMXException, SecurityException, NoSuchMethodException,
			IllegalArgumentException, InvocationTargetException {
		AutoJMX jmxBean = clazz.getAnnotation(AutoJMX.class);
		if (null == jmxBean) {
			jmxBean = autoJMX;
		}
		if (null != jmxBean) {
			String key = clazz.getName();
			if (null != name && !"".equals(name)) {
				key = name;
			}
			Object bean = beans.get(key);
			if (null == bean) {
				if (null != parameterTypes && null != initargs
						&& parameterTypes.length > 0 && initargs.length > 0) {
					Constructor<T> constructor = clazz
							.getConstructor(parameterTypes);
					bean = constructor.newInstance(initargs);
				} else {
					bean = clazz.newInstance();
				}
				beans.put(key, bean);
			} else {
				// 判断是否已经存在此名字
				throw new AutoJMXException("this name: " + key + " is exists!");
			}
			AutoJMXHandler handler = handlers.get(key);
			if (null == handler) {
				handler = new AnnotationAutoJMXHandler(bean, key, jmxBean);
				handler.init();
				handler.start();
				handlers.put(key, handler);
			}
			return (T) bean;
		}
		throw new AutoJMXException("this class: " + clazz.getName()
				+ " must have AutoJMX annotation!");
	}

	/**
	 * 创建Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @param name
	 *            bean name
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> T createJMXBean(T bean, String name, AutoJMX autoJMX)
			throws AutoJMXException {
		AutoJMX jmxBean = bean.getClass().getAnnotation(AutoJMX.class);
		if (null == jmxBean) {
			jmxBean = autoJMX;
		}
		String key = bean.getClass().getName();
		if (null != name && !"".equals(name)) {
			key = name;
		}
		if (null != jmxBean) {
			if (null == bean) {
				throw new AutoJMXException("this bean: " + key
						+ " can not null!");
			}
			if (!beans.containsKey(key)) {
				// 判断是否已经存在此名字
//				throw new AutoJMXException("this name: " + key + " is exists!");
				beans.put(key, bean);
			}
			AutoJMXHandler handler = handlers.get(key);
			if (null == handler) {
				handler = new AnnotationAutoJMXHandler(bean, key, jmxBean);
				handler.init();
				handler.start();
				handlers.put(key, handler);
			}
			return (T) bean;
		}
		throw new AutoJMXException("this class: " + key
				+ " must have AutoJMX annotation!");
	}

	/**
	 * destory Mbean
	 * 
	 * @param <T>
	 * @param name
	 *            bean name
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> void destoryJMXBean(String name) throws InstantiationException,
			IllegalAccessException, AutoJMXException {
		destoryJMXBean(null, name);
	}

	/**
	 * destory Mbean
	 * 
	 * @param <T>
	 * @param clazz
	 *            pojo bean's class
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> void destoryJMXBean(Class clazz) {
		destoryJMXBean(null, clazz.getName());
	}

	/**
	 * destory Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @param clazz
	 *            pojo bean's class
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> void destoryJMXBean(T bean, Class clazz) {
		destoryJMXBean(bean, clazz.getName());
	}

	/**
	 * destory Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @param name
	 *            pojo bean's name
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> void destoryJMXBean(T bean, String name) {
		if (null != beans.get(name)) {
			bean = null;
			beans.remove(name);
			AutoJMXHandler handler = handlers.get(name);
			if (null != handler) {
				handler.stop();
				handler = null;
			}
			handlers.remove(name);
		}
	}

	/**
	 * destory Mbean
	 * 
	 * @param <T>
	 * @param bean
	 *            pojo bean
	 * @param name
	 *            pojo bean's name
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws AutoJMXException
	 */
	public <T> void destoryJMXBeanForAPI(String name) {
		if (!beans.containsKey(name)) {
			for (Map.Entry<String, Object> m : beans.entrySet()) {
				if (m.getValue().getClass().getName().equals(name)) {
					String key = m.getKey();
					
					AutoJMXHandler handler = handlers.get(key);
					if (null != handler) {
						handler.stop();
						handler = null;
					}
					
					if (key.startsWith("_")){
						beans.remove(key);
					}
					handlers.remove(key);
					
				}
			}
		}
		else{
			if (name.startsWith("_")){
				beans.remove(name);
			}
			AutoJMXHandler handler = handlers.get(name);
			if (null != handler) {
				handler.stop();
				handler = null;
			}
			handlers.remove(name);
		}
	}
}
